April 28, 2021
Browser History API를 이용해 구현하는 지하철 노선도 관리 SPA
지하철역을 등록할 수 있다.
지하철역의 이름을 수정할 수 있다.
지하철역을 삭제할 수 있다.
지하철 노선을 등록할 수 있다.
지하철 노선을 수정할 수 있다.
지하철 노선에 구간을 추가, 수정, 삭제할 수 있다.
컴포넌트 단위로 코드를 분리했는데 특정 컴포넌트에서만 쓰이지 않는(범용성 있는) 함수가 특정 컴포넌트의 메서드로 존재하고 있었습니다.
재사용성과 함수의 역할에 대한 부분을 좀 더 고민하면서 코드를 작성해야겠다고 생각했습니다.
컴포넌트 단위로 분리한 Class들에 다음과 같은 로직이 중복적으로 수행되고 있었습니다.
async render(token, sortedSectionList = []) {
$('main').innerHTML = token ? subwayMapTemplate(sortedSectionList) : LOGIN_REQUIRED_TEMPLATE;
}
매번 컴포넌트에서 render로직을 수행할 때 token을 검증하고 그에 따른 로직을 수행하게 한 것입니다.
사실 이런 설계가 나오게 된 배경에는 service layer의 잘못된 분리도 존재했습니다.
service layer에서 API요청을 보낼 때 다음과 같이 token을 외부에서 넘겨받고 있었습니다.
isValidToken: async (token) => {
try {
await API.getUserInfo(token);
return true;
} catch (err) {
console.error(err);
return false;
}
},
service 로직에서 token을 요구하니 이 service 로직을 사용하는 컴포넌트들이 token을 알고 있어야 하는 상황이 왔고 악순환의 반복으로 비슷한 로직을 되풀이하고 있었습니다.
service 로직 token을 자체적으로 얻어오도록 하는 방식으로 중복되는 코드들을 대폭 줄일 수 있었습니다.
기존 component에만 위임했던 render를 다음과 같이 token 유무에 따라 App에서도 직접 렌더하도록 수정했습니다.
// 기존
this.router = {
'/': (token = '') => this.Main.load(token),
'/stations': (token = '') => this.Stations.load(token),
'/lines': (token = '') => this.Lines.load(token),
'/sections': (token = '') => this.Sections.load(token),
'/map': (token = '') => this.SubwayMap.load(token),
'/login': (token = '') => this.Login.load(token),
'/signup': (token = '') => this.Signup.load(token),
}
await this.router[pathName]?.()
// 변경 후
this.router = {
'/': () => this.Main.load(),
'/stations': () => this.Stations.load(),
'/lines': () => this.Lines.load(),
'/sections': () => this.Sections.load(),
'/map': () => this.SubwayMap.load(),
'/login': () => this.Login.load(),
'/signup': () => this.Signup.load(),
}
token ? await this.router[pathName]?.() : this.render()
아직 전체 코드를 설계하고 책임과 역할을 분리시키는 일은 쉽지 않다. 코드를 작성하기 전 layer 분리에 대해 고민하면서 각 layer의 책임과 역할을 명확히 하자는 생각을 했습니다.